#include "engine.h"
#include "shader.h"
#include "file/file.h"
#include <gl/glew.h>

bool common_shader_loaded=false;
char common_src[16384];
char shadersrc[16384];
const char* shaders[2]={common_src,shadersrc};

void shader_t::generate(const string& path)
{
	file_t binsh;
	if (0 && binsh.open((path+".bsh").c_str(),"rb"))
	{
		binsh.read(shadersrc,binsh.size);

		programid=glCreateProgram();
		glProgramBinary(programid, *(GLenum*)shadersrc, shadersrc+4, binsh.size-4);

		GLint success;
		glGetProgramiv(programid, GL_LINK_STATUS, &success);
		ASSERT(success);

		binsh.close();

	}
	else
	{

		if (!common_shader_loaded)
		{
			common_shader_loaded=true;
			file_t cshf("render/shaders/common.sh","rb");
			cshf.read(common_src,cshf.size);
			common_src[cshf.size]=0;
		}

		//vertex
		file_t vshf((path+".vsh").c_str(),"rb");
		shadersrc[vshf.size]=0;
		vshf.read(shadersrc,vshf.size);
		uint32 vshid=glCreateShader(GL_VERTEX_SHADER);
		glShaderSource(vshid,2,shaders,NULL);

		//fragment
		file_t fshf((path+".fsh").c_str(),"rb");
		shadersrc[fshf.size]=0;
		fshf.read(shadersrc,fshf.size);
		uint32 fshid=glCreateShader(GL_FRAGMENT_SHADER);
		glShaderSource(fshid,2,shaders,NULL);

		//geometry, if any
		uint32 gshid=0xffffffff;
		file_t gshf((path+".gsh").c_str(),"rb");

		if (gshf.is_open())
		{
			shadersrc[gshf.size]=0;
			gshf.read(shadersrc,gshf.size);
			gshid=glCreateShader(GL_GEOMETRY_SHADER_EXT);
			glShaderSource(gshid,2,shaders,NULL);
		}

		int p;
		glCompileShader(vshid);
		glGetShaderiv(vshid,GL_COMPILE_STATUS,&p);
		if (!p)
		{
			char str[1024];
			int l;
			glGetShaderInfoLog(vshid,1023,&l,str);
			ASSERTTXT(0,str);
		}
		glCompileShader(fshid);
		glGetShaderiv(fshid,GL_COMPILE_STATUS,&p);
		if (!p)
		{
			char str[1024];
			int l;
			glGetShaderInfoLog(fshid,1023,&l,str);
			ASSERTTXT(0,str);
		}

		if (gshid!=0xffffffff)
		{
			glCompileShader(gshid);
			glGetShaderiv(gshid,GL_COMPILE_STATUS,&p);
			if (!p)
			{
				char str[1024];
				int l;
				glGetShaderInfoLog(fshid,1023,&l,str);
				ASSERTTXT(0,str);
			}
		}


		programid=glCreateProgram();

		glAttachShader(programid,vshid);
		GLASSERT;
		glAttachShader(programid,fshid);
		GLASSERT;

		if (gshid!=(uint32)-1)
		{
			glAttachShader(programid,gshid);
			GLASSERT;
			glProgramParameteriEXT(programid,GL_GEOMETRY_INPUT_TYPE_EXT,GL_POINTS);
			glProgramParameteriEXT(programid,GL_GEOMETRY_OUTPUT_TYPE_EXT,GL_TRIANGLE_STRIP);
			glProgramParameteriEXT(programid,GL_GEOMETRY_VERTICES_OUT_EXT,4);
		}

	//	glBindAttribLocation(programid, 0, "pos");

		glProgramParameteri(programid, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE);	
		glLinkProgram(programid);
		GLASSERT;


		int binarysize=0;
		glGetProgramiv(programid, GL_PROGRAM_BINARY_LENGTH, &binarysize);
		char* binary = new char[binarysize+4];
		glGetProgramBinary(programid, binarysize, NULL, (GLenum*)binary, binary+4);
		binsh.open((path+".bsh").c_str(),"wb");
		binsh.write(binary,binarysize+4);
		binsh.close();
		delete [] binary;


	}

	uint32 globaldataid=glGetUniformBlockIndex(programid,"global");
	if (globaldataid!=0xffffffff)
	{
		GLASSERT;
		int32 s;
		glGetActiveUniformBlockiv(programid,globaldataid,GL_UNIFORM_BLOCK_DATA_SIZE,&s);
		GLASSERT;
		glUniformBlockBinding(programid,globaldataid,0);
		GLASSERT;
	}

	glUseProgram(programid);
	glUniform1i(glGetUniformLocation(programid,"texture"),0);
//	glUniform1i(0,0);
	GLASSERT;
//	glUniform1i(1,1);
	glUniform1i(glGetUniformLocation(programid,"texture1"),1);
	GLASSERT;
}

void shader_t::set()
{
	glUseProgram(programid);
}

shader_t::~shader_t()
{
	glDeleteProgram(programid);
}